package l2ft.gameserver.model.entity.events.fightclubmanager;

import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.CopyOnWriteArrayList;

import javax.xml.parsers.DocumentBuilderFactory;

import javolution.util.FastMap;
import l2ft.commons.threading.RunnableImpl;
import l2ft.commons.util.Rnd;
import l2ft.gameserver.Config;
import l2ft.gameserver.ThreadPoolManager;
import l2ft.gameserver.model.Creature;
import l2ft.gameserver.model.Effect;
import l2ft.gameserver.model.Playable;
import l2ft.gameserver.model.Player;
import l2ft.gameserver.model.Skill;
import l2ft.gameserver.model.entity.events.impl.AbstractFightClub;
import l2ft.gameserver.model.entity.events.impl.AbstractFightClub.MESSAGE_TYPES;
import l2ft.gameserver.network.l2.components.ChatType;
import l2ft.gameserver.network.l2.s2c.Say2;
import l2ft.gameserver.utils.Util;

import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.InputSource;

public class FightClubAchievements
{
	//TODO statistics how long it takes to complete achievement - for me only - so i can balance the rewards
	private static FightClubAchievements _instance;
	private List<FightClubAchievement> _achievements = new ArrayList<>();
	
	private List<FightClubPlayer> _timedAchievements = new CopyOnWriteArrayList<>();
	private boolean _timedAchievementsThreadStarted;
	
	public enum ACHIEVEMENT_TARGET_TYPE {
		NONE,
		PLAYER,
		PET,
		MONSTER,
		POINT,
		FLAG,
		RIFT,
		BOSS,
		GUARD,
		CAPTAIN,
		CRYSTAL,
		CHEST
	}
	
	public enum ACHIEVEMENT_TARGET_REQUIREMENT {
		ANY,
		SPECIFIC,
		LEAGUE,
		TEAM,
		RACE
	}
	
	public enum ACHIEVEMENT_ACTION {
		NONE,
		NORMAL_KILL,
		KILL_AND_HAVE_EFFECT,
		KILL_WITH_SKILL,
		LAND_EFFECT,
		BUFF,
		HEAL,
		EVENT_SPECIFIC_ACTION
	}
	
	public enum ACHIEVEMENT_COMBO_BREAKERS {
		NONE,
		MY_DEATH,
		MY_DEATH_OVER,
		TARGET_DEATH_OVER,
		ONE_TRY,//LAND_EFFECT
		TIME_OVER,//can be also good breaker
		EVENT_SPECIFIC_COMBO
	}
	
	public enum ACHIEVEMENT_ATTACKER {
		PLAYER,
		PET
	}
	
	public FightClubAchievements()
	{
		loadAchievements();
	}
	
	/**
	 * TellingAboutDeath to players with listeners. Checking MY_DEATH bad_breaker. Supporting NORMAL_KILL, KILL_AND_HAVE_EFFECT, KILL_WITH_SKILL
	 */
	public void onKilled(Creature killer, Creature victim, Skill skill)
	{
		for(Player iPlayer : victim.getTellAboutDeathToPlayers())
		{
			if(iPlayer == null || !iPlayer.isOnline() || !iPlayer.isInFightClub())
				continue;
			tellingAboutDeath(killer, victim, iPlayer);
		}
		
		if(victim.isPlayer())
		{
			AbstractFightClub event = victim.getPlayer().getFightClubEvent();;
			FightClubPlayer fPlayer = event.getFightClubPlayer(victim);
			
			if(fPlayer.getAchProgress() != null)
			{
				if(fPlayer.getAchievement().getBadComboBreaker() == ACHIEVEMENT_COMBO_BREAKERS.MY_DEATH)
				{
					if(fPlayer.getAchievementCurrentProgress() > 0)
					{
						victim.sendPacket(new Say2(victim.getObjectId(), ChatType.COMMANDCHANNEL_ALL, "Achievement", "Combo Broken!"));
					}
					
					fPlayer.breakProgress();
					event.sendAchievementMessage(fPlayer, false, true);
				}
				else if(fPlayer.getAchievement().getBadComboBreaker() == ACHIEVEMENT_COMBO_BREAKERS.MY_DEATH_OVER)
				{
					achievementFailed(fPlayer);
				}
			}
		}
		
		if(killer.isPlayable())
		{
			AbstractFightClub event = killer.getPlayer().getFightClubEvent();;
			FightClubPlayer fPlayer = event.getFightClubPlayer(killer);
			
			if(fPlayer.getAchProgress() == null)
				return;
			
			switch(fPlayer.getAchievement().getAction())
			{
				case NORMAL_KILL:
					break;
				case KILL_AND_HAVE_EFFECT:
					boolean found = false;
					Skill skillToHave = (Skill)fPlayer.getAchievementAction();
					for(Effect e : killer.getEffectList().getAllEffects())
						if(e.getSkill().getId() == skillToHave.getId())
						{
							found = true;
							break;
						}
					if(!found)
						return;
					break;
				case KILL_WITH_SKILL:
					skillToHave = (Skill)fPlayer.getAchievementAction();
					System.out.println("skill to have:"+skillToHave+" used skill:"+skill);
					if(!skillToHave.equals(skill))
						return;
					break;
				default:
					return;
			}
			
			if(!checkIfCanAddProgress((Playable)killer, victim))
				return;
			
			//Passed exam
			addProgress(fPlayer);
		}
	}
	
	/**
	 * Handling LAND_EFFECT. Supporting ONE_TRY. Adding progress if everything is fine
	 */
	public void debuffPlayers(Playable attacker, Skill skill, List<Creature> landedOn)
	{
		Player player = attacker.getPlayer();
		AbstractFightClub event = player.getFightClubEvent();
		FightClubPlayer fPlayer = event.getFightClubPlayer(player);
		
		System.out.println("landed on:"+landedOn.size());
		if(fPlayer.getAchProgress() == null)
			return;
		
		Skill actionSkill = (Skill)fPlayer.getAchievementAction();
		if(!actionSkill.equals(skill))
			return;
		System.out.println("skill passed");
		if(fPlayer.getAchievement().getAction() != ACHIEVEMENT_ACTION.LAND_EFFECT)
		{
			return;
		}
		
		int howManyReallyLanded = 0;
		for(Creature c : landedOn)
			if(checkIfCanAddProgress(attacker, c))
				howManyReallyLanded++;
		
		boolean isOneTry = fPlayer.getAchievement().getBadComboBreaker() == ACHIEVEMENT_COMBO_BREAKERS.ONE_TRY;
		System.out.println("Before adding progress");
		if(!isOneTry ||howManyReallyLanded > fPlayer.getAchievementMaxProgress())
			addProgress(fPlayer, howManyReallyLanded);
	}
	
	/**
	 * Used just for HEAL. Adding healed to getHealed() and getTellAboutDeathToPlayers(). Handling addingProgress if healed was healed to max hp
	 */
	public void healPlayer(Playable healer, Playable healed, int healedHp)
	{
		AbstractFightClub event = healer.getPlayer().getFightClubEvent();
		FightClubPlayer fPlayer = event.getFightClubPlayer(healer);

		if(fPlayer.getAchProgress() == null)
			return;
		
		if(fPlayer.getAchievement().getAction() != ACHIEVEMENT_ACTION.HEAL)
		{
			return;
		}
		
		if(!checkIfCanAddProgress(healer, healed))
			return;
		
		if(!fPlayer.getHealed().contains(healed.getPlayer()))
		{
			if(healed.getCurrentHp() - healedHp < healed.getMaxHp()/2)
			{
				fPlayer.getHealed().add(healed.getPlayer());
				healed.getTellAboutDeathToPlayers().add(healer.getPlayer());
			}
			return;
		}
		else
		{
			if(healed.getCurrentHp() < healed.getMaxHp())
				return;
		}

		fPlayer.getHealed().remove(healed);
		addProgress(fPlayer, 1);
	}
	
	/**
	 * Handling BUFF action. If everything is ok, adding progress.
	 */
	public void buffPlayer(Playable buffer, Creature buffed, Skill skill)
	{
		AbstractFightClub event = buffer.getPlayer().getFightClubEvent();
		FightClubPlayer fPlayer = event.getFightClubPlayer(buffer);
		
		if(fPlayer.getAchProgress() == null)
			return;
		
		if(fPlayer.getAchievement().getAction() != ACHIEVEMENT_ACTION.BUFF)
		{
			return;
		}
		
		if(!checkIfCanAddProgress(buffer, buffed))
			return;
	
		addProgress(fPlayer, 1);
	}
	
	/**
	 * @Used for HEAL
	 * Removing player from getHealed() list. Handling TARGET_DEATH bad_breaker
	 */
	public void tellingAboutDeath(Creature killer, Creature deadGuy, Player hasTheListener)
	{
		AbstractFightClub event = hasTheListener.getFightClubEvent();
		FightClubPlayer fPlayer = event.getFightClubPlayer(hasTheListener);
		
		if(fPlayer.getAchProgress() == null)
			return;
		
		//It is handled in onKilled()
		if(fPlayer.getAchievement().getBadComboBreaker() == ACHIEVEMENT_COMBO_BREAKERS.MY_DEATH)
			return;
		
		if(!deadGuy.isPlayer())
			fPlayer.getHealed().remove(deadGuy.getPlayer());
		
		if(fPlayer.getAchievement().getBadComboBreaker() == ACHIEVEMENT_COMBO_BREAKERS.TARGET_DEATH_OVER)
		{
			achievementFailed(fPlayer);
		}
	}

	/**
	 * Checking targetType, targetRequirement, attacker
	 * @return true if everything is fine
	 */
	private boolean checkIfCanAddProgress(Playable actor, Creature victim)
	{
		Player player = actor.getPlayer();
		
		if(actor.getPlayer() != null)
		{
			//Killer is player or pet
			
			AbstractFightClub event = player.getFightClubEvent();
			FightClubPlayer fPlayer = event.getFightClubPlayer(player);
			
			System.out.println("before check if fits target type");
			if(!event.checkAchievementFitsTargetType(fPlayer, victim))
				return false;
			System.out.println("before checking requirements");
			if(!checkTargetRequirement(fPlayer, victim, event))
				return false;
			System.out.println("before checking attacker");
			if(!checkAttacker(actor, fPlayer))
				return false;
		}
		return true;
	}
	
	/**
	 * Adding progress, checking if it was completed. Handling new achievement if it was
	 * @return was achievement completed
	 */
	private boolean addProgress(FightClubPlayer fPlayer, Integer... by)
	{
		int addToProgress = by.length > 0 ? by[0] : 1;
		boolean finished = fPlayer.increaseAchievementProgress(addToProgress);
		
		if(finished)
		{
			achievementCompleted(fPlayer);
		}
		else
		{
			fPlayer.getEvent().sendAchievementMessage(fPlayer, false, true);
		}
		
		return finished;
	}
	
	/**
	 * Setting achievementFailed, sending message and starts new achievement timer
	 */
	private void achievementFailed(FightClubPlayer fPlayer)
	{
		removeTarget(fPlayer);
		addAchievementToStats(fPlayer, false);
		fPlayer.achievementFailed();
		fPlayer.getEvent().sendMessageToPlayer(fPlayer, MESSAGE_TYPES.SCREEN_BIG, "Achievement Failed!");
		fPlayer.getEvent().waitAndStartAchievement(fPlayer);
		fPlayer.getEvent().sendAchievementMessage(fPlayer, false, false);
	}
	
	/**
	 * Setting achievementCompleted, sending message and starts new achievement timer
	 */
	private void achievementCompleted(FightClubPlayer fPlayer)
	{
		removeTarget(fPlayer);
		addAchievementToStats(fPlayer, true);
		fPlayer.completeAchievement();
		fPlayer.getEvent().sendMessageToPlayer(fPlayer, MESSAGE_TYPES.SCREEN_BIG, "Achievement Completed!");
		fPlayer.getEvent().waitAndStartAchievement(fPlayer);
		fPlayer.getEvent().sendAchievementMessage(fPlayer, false, false);
	}
	
	private void removeTarget(FightClubPlayer fPlayer)
	{
		if(fPlayer.getAchievement().getTargetRequirements() == ACHIEVEMENT_TARGET_REQUIREMENT.SPECIFIC)
		{
			Creature target = (Creature) fPlayer.getTarget();
			target.getInvidualAbnormals().remove(fPlayer.getPlayer());
			target.broadcastCharInfo();
			if(target.isPlayer() && target.getPlayer().getPet() != null)
				target.getPet().broadcastCharInfo();
		}
	}
	
	/**
	 * Adding achievement to FightClubLastStatsManager
	 */
	public void addAchievementToStats(FightClubPlayer fPlayer, boolean success)
	{
		FightClubAchievement ach = fPlayer.getAchievement();
		AbstractFightClub event = fPlayer.getEvent();
		System.out.println("achievement start time:"+fPlayer.getAchievementStartTime());
		int totalTime = (int)(System.currentTimeMillis()-fPlayer.getAchievementStartTime())/1000;
		FightClubLastStatsManager.setNewStat(fPlayer.getPlayer(), ach.getName(), (success ? 1 : 0), (success ? getReward(ach, event) : 0), totalTime);
	}
	
	/**
	 * @return Achievement needs additional object as "Action"?
	 */
	private boolean achievementNeedsAction(FightClubAchievement ach)
	{
		switch(ach.getAction())
		{
			case BUFF:
			case HEAL:
			case KILL_AND_HAVE_EFFECT:
			case KILL_WITH_SKILL:
			case LAND_EFFECT:
				return true;
			default:
				return false;
		}
	}
	
	/*
	 * Timed achievements
	 */
	
	/*
	 * Getters
	 */
	public FightClubAchievement getAchivementByName(String name)
	{
		for(FightClubAchievement achievement : _achievements)
			if(achievement.getName().equals(name))
				return achievement;
		return null;
	}
	
	/**
	 * @return random Achievement that player didn't take before and he is able to
	 */
	public FightClubAchievement getAchievementForEvent(AbstractFightClub event, FightClubPlayer fPlayer)
	{
		final String changedName = Util.getChangedEventName(event);
		List<FightClubAchievement> notCompletedAchievements = new ArrayList<>();
		
		for(FightClubAchievement achievement : _achievements)
		{
			if(!achievement.getEventsPoints().containsKey(changedName))
				continue;
			if(fPlayer.wasAchievementTaken(achievement))
				continue;
			if(!event.achievementTargetExists(achievement, fPlayer))
				continue;
			if(!achievementNeedsAction(achievement) || event.getActionForAchievement(achievement, fPlayer) != null)
			{
				System.out.println("Adding achievement");
				notCompletedAchievements.add(achievement);
			}
		}
		
		if(notCompletedAchievements.size() > 0)
			return Rnd.get(notCompletedAchievements);
		else
			return null;
	}

	/**
	 * @return number of badges that player earns from achievement
	 */
	public int getReward(FightClubAchievement achievement, AbstractFightClub fightClubEvent)
	{
		return achievement.getEventsPoints().get(Util.getChangedEventName(fightClubEvent));
	}	
	
	/**
	 * Getting original name of achievement and replacing %time%, %target% etc
	 */
	public String getChangedName(FightClubPlayer fPlayer)
	{
		int currentProgress = fPlayer.getAchievementCurrentProgress();
		int progressLeft = fPlayer.getAchievementMaxProgress() - currentProgress;
		String firstName = fPlayer.getAchievement().getName();
		
		firstName = firstName.replace("%count%", ""+progressLeft);
		firstName = firstName.replace("%s%", (progressLeft > 1 ? "s" : ""));
		
		if(firstName.contains("%time%"))
			firstName = firstName.replace("%time%", ""+getChangedTime(fPlayer.getAchievementTimeOver()));
		if(firstName.contains("%target%"))
			firstName = firstName.replace("%target%", fPlayer.getEvent().getAchievementTargetName(fPlayer));
		if(firstName.contains("%action%"))
		{
			Object obj = fPlayer.getAchievementAction();
			if(obj instanceof Skill)
				firstName = firstName.replace("%action%", ((Skill)obj).getName());
		}
		
		return firstName;
	}	
	
	/**
	 * Getting original name of achievement and replacing %time%, %target% etc
	 */
	public String getChangedName(AbstractFightClub event, String achievementName)
	{
		FightClubAchievement ach = getAchivementByName(achievementName);
		int progressLeft = getMaxProgress(ach, event);
		
		achievementName = achievementName.replace("%count%", ""+progressLeft);
		achievementName = achievementName.replace("%s%", (progressLeft > 1 ? "s" : ""));
		
		if(achievementName.contains("%time%"))
			achievementName = achievementName.replace("%time%", ""+getChangedTime(getMaxTime(ach, event)));
		if(achievementName.contains("%target%"))
			achievementName = achievementName.replace("%target%", "<target>");
		System.out.println("Contains action:"+achievementName.contains("%action%"));
		if(achievementName.contains("%action%"))
		{
			achievementName = achievementName.replace("%action%", "<skill>");
		}
		
		System.out.println("name after change:"+achievementName);
		
		return achievementName;
	}
	
	/**
	 * @return max progress, if player will reach it - achievement will be completed
	 */
	public int getMaxProgress(FightClubAchievement achievement, AbstractFightClub event)
	{
		String eventName = Util.getChangedEventName(event);
		
		if(achievement.getName().contains("%count%"))
			for(Entry<String, Integer> entry : achievement.getCountReplacements().entrySet())
				if(entry.getKey().equals(eventName))
					return entry.getValue();
							
		return 1;
	}
	
	/**
	 * @return time when achievement should be over
	 */
	public int getMaxTime(FightClubAchievement achievement, AbstractFightClub event)
	{
		String eventName = Util.getChangedEventName(event);
		
		if(achievement.getName().contains("%time%"))
			for(Entry<String, Integer> entry : achievement.getTimeReplacements().entrySet())
				if(entry.getKey().equals(eventName))
					return entry.getValue();
							
		return -1;
	}
	
	/*
	 * Timed achievements
	 */
	
	private boolean isTimedAchievement(FightClubAchievement ach)
	{
		return ach.getBadComboBreaker() == ACHIEVEMENT_COMBO_BREAKERS.TIME_OVER || ach.getGoodComboBreaker() == ACHIEVEMENT_COMBO_BREAKERS.TIME_OVER;
	}
	
	public synchronized void startedNewAchievement(FightClubPlayer fPlayer)
	{
		System.out.println("startedNewAchievement");
		if(isTimedAchievement(fPlayer.getAchievement()))
		{
			_timedAchievements.add(fPlayer);
			if(!_timedAchievementsThreadStarted)
			{
				System.out.println("starting TimedAchievementsThread thread");
				ThreadPoolManager.getInstance().schedule(new TimedAchievementsThread(), 1000);
				_timedAchievementsThreadStarted = true;
			}
		}
	}

	/**
	 * Used for TimedAchievementsThread, don't change
	 */
	private static final int[] SECONDS_WHEN_CHANGE = {30, 15, 10, 5, 4, 3, 2, 1};
	
	private class TimedAchievementsThread extends RunnableImpl
	{
		@Override
		public void runImpl() throws Exception
		{
			List<FightClubPlayer> toDelete = new ArrayList<>();
			for(FightClubPlayer iFPlayer : _timedAchievements)
			{
				//Player logged out or he doesnt have active achievement
				if(!iFPlayer.getPlayer().isOnline() || iFPlayer.getAchProgress() == null)
				{
					toDelete.add(iFPlayer);
					continue;
				}
					
				int secondsLeft = iFPlayer.getAchievementTimeOver()-1;
					
				System.out.println("Seconds left:"+secondsLeft);
					
				//Time is up
				if(secondsLeft <= 0)
				{
					if(iFPlayer.getAchievement().getGoodComboBreaker() == ACHIEVEMENT_COMBO_BREAKERS.TIME_OVER)
					{
						achievementCompleted(iFPlayer);
					}
					else if(iFPlayer.getAchievement().getBadComboBreaker() == ACHIEVEMENT_COMBO_BREAKERS.TIME_OVER)
					{
						achievementFailed(iFPlayer);
					}
					toDelete.add(iFPlayer);
					continue;
				}
					
				if(!iFPlayer.isAfk())
				{
					//Updating time value
					iFPlayer.setAchievementTimeOver(secondsLeft);
				}
				else//Time is stopped for afk players
				{
					continue;
				}
					
				//Checking if achievement message should be updated
				boolean shouldChange = secondsLeft % 60 == 0;
				if(secondsLeft < 60)
					for(int i : SECONDS_WHEN_CHANGE)
						if(secondsLeft == i)
							shouldChange = true;
					
				//If it should, updating message
				System.out.println("should change:"+shouldChange);
				if(shouldChange)
				{
					iFPlayer.getEvent().sendAchievementMessage(iFPlayer, false, false);
				}
			}
			
			for(FightClubPlayer del : toDelete)
				_timedAchievements.remove(del);
				
			//Starting new thread in 900 miliseconds
			ThreadPoolManager.getInstance().schedule(new TimedAchievementsThread(), 900);
		}
	}
	

	public class FightClubAchievement
	{
		private final String _name;
		private ACHIEVEMENT_TARGET_TYPE _targetType;
		private ACHIEVEMENT_TARGET_REQUIREMENT _targetReq;
		private ACHIEVEMENT_ATTACKER _attacker;
		private ACHIEVEMENT_ACTION _action;
		private ACHIEVEMENT_COMBO_BREAKERS _badComboBreaker;
		private ACHIEVEMENT_COMBO_BREAKERS _goodComboBreaker;
		
		
		private final Map<String, Integer> _eventsPoints;
		private final Map<String, Integer> _countReplacements;
		private final Map<String, Integer> _timeReplacements;
		
		private FightClubAchievement(String name, Map<String, Integer> eventsPoints, Map<String, Integer> countReplacements, Map<String, Integer> timeReplacements)
		{
			_name = name;
			_eventsPoints = eventsPoints;
			_countReplacements = countReplacements;
			_timeReplacements = timeReplacements;
		}
		
		public String getName()
		{
			return _name;
		}
		
		public ACHIEVEMENT_TARGET_TYPE getTargetType()
		{
			return _targetType;
		}
		
		public ACHIEVEMENT_TARGET_REQUIREMENT getTargetRequirements()
		{
			return _targetReq;
		}
		
		public ACHIEVEMENT_ACTION getAction()
		{
			return _action;
		}
		
		public ACHIEVEMENT_ATTACKER getAttacker()
		{
			return _attacker;
		}
		
		public ACHIEVEMENT_COMBO_BREAKERS getBadComboBreaker()
		{
			return _badComboBreaker;
		}
		
		public ACHIEVEMENT_COMBO_BREAKERS getGoodComboBreaker()
		{
			return _goodComboBreaker;
		}
		
		public Map<String, Integer> getEventsPoints()
		{
			return _eventsPoints;
		}
		
		public Map<String, Integer> getCountReplacements()
		{
			return _countReplacements;
		}
		
		public Map<String, Integer> getTimeReplacements()
		{
			return _timeReplacements;
		}
		
		private void setTargetType(String type)
		{
			if(type != null)
				_targetType = ACHIEVEMENT_TARGET_TYPE.valueOf(type);
		}
		private void setTargetReq(String targetReq)
		{
			if(targetReq != null)
				_targetReq = ACHIEVEMENT_TARGET_REQUIREMENT.valueOf(targetReq);
		}
		private void setAttacker(String attacker)
		{
			if(attacker != null)
				_attacker = ACHIEVEMENT_ATTACKER.valueOf(attacker);
		}
		private void setAction(String action)
		{
			if(action != null)
				_action = ACHIEVEMENT_ACTION.valueOf(action);
		}
		private void setGoodComboBreaker(String comboBreaker)
		{
			if(comboBreaker != null)
				_goodComboBreaker = ACHIEVEMENT_COMBO_BREAKERS.valueOf(comboBreaker);
		}
		private void setBadComboBreaker(String comboBreaker)
		{
			if(comboBreaker != null)
				_badComboBreaker = ACHIEVEMENT_COMBO_BREAKERS.valueOf(comboBreaker);
		}
	}
	
	//TODO "Stun 5 players" - atm it searches for skills that are just debuffs
	private boolean checkTargetRequirement(FightClubPlayer fPlayer, Creature victim, AbstractFightClub event)
	{
		Object target = fPlayer.getTarget();
		switch(fPlayer.getAchievement().getTargetRequirements())
		{
		case ANY:
			return true;
		case LEAGUE:
			//TODO
			break;
		case RACE:
			return target.equals(victim.getPlayer().getRace());
		case SPECIFIC:
			return target.equals(victim);
		case TEAM:
			return event.getFightClubPlayer(victim).getTeam().equals(target);
		}
		return false;
	}
	
	private boolean checkAttacker(Creature c, FightClubPlayer fPlayer)
	{
		switch(fPlayer.getAchievement().getAttacker())
		{
		case PET:
			return c.isSummon();
		case PLAYER:
			return c.isPlayer();
		}
		return false;
	}
	
	private String getChangedTime(int secondsToEnd)
	{
		if(secondsToEnd <= 0)
			return "";
		
		if(secondsToEnd >= 60)
		{
			int minutes = (int)Math.round(((double)secondsToEnd / 60));
			return minutes+" minute"+(minutes > 1 ? "s" : "");
		}
		else
		{
			return secondsToEnd+" second"+(secondsToEnd > 1 ? "s" : "");
		}
	}
	
	private void loadAchievements()
	{
		DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
		factory.setValidating(false);
		factory.setIgnoringComments(true);
		
		File file = new File(Config.DATAPACK_ROOT + "/data/xml/other/fight_club_achievements.xml");
		
		try
		{
			InputSource in = new InputSource(new InputStreamReader(new FileInputStream(file), "UTF-8"));
			in.setEncoding("UTF-8");
			Document doc = factory.newDocumentBuilder().parse(in);
			
			for (Node n = doc.getFirstChild(); n != null; n = n.getNextSibling())
			{
				if (n.getNodeName().equalsIgnoreCase("list"))
				{
					for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
					{
						if (d.getNodeName().equalsIgnoreCase("achievement"))
						{
							String name = d.getAttributes().getNamedItem("name").getNodeValue();
							
							String targetType = getAttribute(d, "targetType");
							String targetReq =  getAttribute(d, "targetReq");
							String attacker =  getAttribute(d, "attacker");
							String action =  getAttribute(d, "action");
							String badBreaker =  getAttribute(d, "bad_breaker");
							String goodBreaker =  getAttribute(d, "good_breaker");
							
							Map<String, Integer> events = new FastMap<>();
							Map<String, Integer> countReplacements = new FastMap<>();
							Map<String, Integer> timeReplacements = new FastMap<>();
							
							for (Node a = d.getFirstChild(); a != null; a = a.getNextSibling())
								if (a.getNodeName().equalsIgnoreCase("event"))
								{
									String eventName = a.getAttributes().getNamedItem("name").getNodeValue();
									Integer eventPoints = Integer.parseInt(a.getAttributes().getNamedItem("points").getNodeValue());
									events.put(eventName, eventPoints);
									
									if(a.getAttributes().getNamedItem("count") != null)
									{
										String count = a.getAttributes().getNamedItem("count").getNodeValue();
										countReplacements.put(eventName, Integer.parseInt(count));
									}
									
									if(a.getAttributes().getNamedItem("time") != null)
									{
										String time = a.getAttributes().getNamedItem("time").getNodeValue();
										timeReplacements.put(eventName, Integer.parseInt(time));
									}
								}
							
							FightClubAchievement achievement = new FightClubAchievement(name, events, countReplacements, timeReplacements);
							
							achievement.setTargetType(targetType);
							achievement.setTargetReq(targetReq);
							achievement.setAttacker(attacker);
							achievement.setAction(action);
							achievement.setBadComboBreaker(badBreaker);
							achievement.setGoodComboBreaker(goodBreaker);
							
							_achievements.add(achievement);
						}
					}
				}
			}
		}
		catch (Exception e)
		{
			e.printStackTrace();
		}
	}
	
	private String getAttribute(Node d, String name)
	{
		if(d.getAttributes().getNamedItem(name) != null)
			return d.getAttributes().getNamedItem(name).getNodeValue();
		return null;
	}
	
	public static FightClubAchievements getInstance()
	{
		if(_instance == null)
			_instance = new FightClubAchievements();
		return _instance;
	}
}
